package main

import (
	"context"
	"flag"
	"fmt"
	"gRPC_Server/helper"
	"gRPC_Server/pbfiles"
	"gRPC_Server/service"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"net/http"
	"time"

	"log"
	"net"
)

const (
	secretKey     = "secret"
	tokenDuration = 15 * time.Minute
	gRPCAddress   = "127.0.0.1:8888"
	restAddress   = "127.0.0.1:8080"
)

func createUser(userStore service.UserStore, username, password, role string) error {
	user, err := service.NewUser(username, password, role)
	if err != nil {
		return err
	}
	return userStore.Save(user)
}
func seedUsers(userStore service.UserStore) error {
	err := createUser(userStore, "admin", "secret", "admin")
	if err != nil {
		return err
	}
	return createUser(userStore, "user", "secret", "user")
}
func accessibleRoles() map[string][]string {
	const laptopServicePath = "/services.LaptopService/"

	return map[string][]string{
		laptopServicePath + "CreateLaptop": {"admin"},
		laptopServicePath + "UploadImage":  {"admin"},
		laptopServicePath + "RateLaptop":   {"admin", "user"},
	}
}
func runGRPCServer(
	authServer pbfiles.AuthServiceServer,
	laptopServer pbfiles.LaptopServiceServer,
	jwtManager *service.JWTManager,
	enableTLS bool,
	listener net.Listener) error {
	interceptor := service.NewAuthInterceptor(jwtManager, accessibleRoles())
	serverOptions := []grpc.ServerOption{
		grpc.UnaryInterceptor(interceptor.Unary()),
		grpc.StreamInterceptor(interceptor.Stream()),
	}
	if enableTLS {
		tlsCreDentials, err := helper.GetServeCreds()
		if err != nil {
			return fmt.Errorf("cannot load TLS credentials: %w", err)
		}
		serverOptions = append(serverOptions, grpc.Creds(tlsCreDentials))
	}
	grpcServer := grpc.NewServer(serverOptions...)

	pbfiles.RegisterAuthServiceServer(grpcServer, authServer)
	pbfiles.RegisterLaptopServiceServer(grpcServer, laptopServer)
	//reflection.Register(grpcServer)

	log.Printf("Start GRPC server at %s, TLS = %t", listener.Addr().String(), enableTLS)
	return grpcServer.Serve(listener)
}

func runRestServer(enableTLS bool) error {
	gwmux := runtime.NewServeMux() //创建路由
	dialOptions := []grpc.DialOption{}
	if enableTLS {
		tlsCreDentials, err := helper.GetServeCreds()
		if err != nil {
			return fmt.Errorf("cannot load TLS credentials: %w", err)
		}
		dialOptions = append(dialOptions, grpc.WithTransportCredentials(tlsCreDentials))
	} else {
		dialOptions = append(dialOptions, grpc.WithTransportCredentials(insecure.NewCredentials()))
	}
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	err := pbfiles.RegisterAuthServiceHandlerFromEndpoint(ctx, gwmux, gRPCAddress, dialOptions) // 将http请求转为grpc请求
	if err != nil {
		return err
	}

	err = pbfiles.RegisterLaptopServiceHandlerFromEndpoint(ctx, gwmux, gRPCAddress, dialOptions)
	if err != nil {
		return err
	}
	log.Printf("Start REST server at %s, TLS = %t", restAddress, enableTLS)
	httpServer := &http.Server{
		Addr:    restAddress, //暴露给http服务的端口地址
		Handler: gwmux,
	}
	return httpServer.ListenAndServe()
}

func main() {
	enableTLS := flag.Bool("tls", true, "enable SSL/TLS")
	serverType := flag.String("type", "grpc", "type of server (grpc/rest)")
	flag.Parse()

	userStore := service.NewInMemoryUserStore()
	if err := seedUsers(userStore); err != nil {
		log.Fatal("cannot seed users:", err)
	}
	jwtManager := service.NewJWTManager(secretKey, tokenDuration)
	authServer := service.NewAuthServer(userStore, jwtManager)

	laptopStore := service.NewInMemoryLaptopStore()
	imageStore := service.NewDiskImageStore("E:\\5120154230GoCode\\src\\gRPC_Server\\img")
	ratingStore := service.NewInMemoryRatingStore()
	laptopServer := service.NewLaptopServer(laptopStore, imageStore, ratingStore)

	listener, err := net.Listen("tcp", gRPCAddress) //设置传输协议和监听地址
	if err != nil {
		log.Fatal("cannot start server: ", err)
	}
	if *serverType == "grpc" {
		err = runGRPCServer(authServer, laptopServer, jwtManager, *enableTLS, listener)
	} else {
		err = runRestServer(*enableTLS)
	}
	if err != nil {
		log.Fatal("cannot start server: ", err)
	}
}
